package helper.scxml.scxml2.zone.t2.case4_

import helper.base.A2LHM
import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.LHMHelper.A2LHMExpand.makeA2LHM
import helper.base.LHMHelper.A3LHM.Companion.makeA3LHM
import helper.base.ZoneConstraintHelper
import helper.base.math.TreeHelper.T2HierarchyStructure
import helper.base.math.TreeHelper.T2HierarchyStructure.Companion.makeT2ParentChildRelations
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config.ConfigBoolFun.aConfigTrueFun
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config.ConfigBoolFun.aHeapNotContainsEventFun
import helper.scxml.scxml2.zone.t2.NoteRunResultFun
import helper.scxml.scxml2.zone.t2.ScxmlEnvHelper.ScxmlEnv
import helper.scxml.scxml2.zone.t2.T3SNodeUnitHelper.ERT3SNodeUnit
import helper.scxml.scxml2.zone.t2.T3SNodeUnitHelper.ERT3SNodeUnit.Companion.makeERT3SNodeUnit
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.AssignAction.Companion.makeAssignAction
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.DoubleDoubleFunTuple.Companion.makeDoubleDoubleFunTuple
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.DoubleListDoubleFunTuple.Companion.makeDoubleListDoubleFunTuple
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.Edge
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.Edge.Companion.makeEdge
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.IAction
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.SendAction.Companion.makeSendAction
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.StringActionListLHM
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.StringActionListLHM.Companion.makeStringActionListLHM
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.VoidDoubleFunTuple.Companion.makeVoidDoubleFunTuple
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.ZoneEnv.Companion.tClock
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.tNoteRunResultFun
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.ConveyorBelt.Companion.makeConveyorBelt
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.CupGenerator.Companion.makeCupGenerator
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.LidTuple.Companion.makeLidTuple
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.MechanicalArm.Companion.makeMechanicalArm
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.StrategyObj.Env.getT1StringEnvStrategyLHM
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.StrategyObj.Ren.getT1StringRenStrategyLHM
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.T4Env.Companion.aDevsNames
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.T4Env.Companion.s1_cupGenerator
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.T4Env.Companion.s2_conveyorBelt
import helper.scxml.scxml2.zone.t2.case4_.T4EnvHelper.T4Env.Companion.s3_mechanicalArm
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent.Companion.makeTimedEvent
import helper.scxml.strategy.TotalStrategyHelper.KREWTuple.Companion.makeKREWTuple
import helper.scxml.strategy.TotalStrategyHelper.REWTuple.Companion.makeREWTuple
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.SubT2SNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.SubT2SNodeUnit.Companion.makeSubT2SNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.TimedEventWeightTable
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.TimedEventWeightTable.Companion.makeTimedEventWeightTable
import kotlin.math.cos

object T4EnvHelper {
    interface IDev {
        fun getDevName(): String
    }

    class CupGenerator(
        val name: String,
        val cupTypeNum: Int = 4,
    ) : IDev {
        val eventNamePrefix = "e1"
        val eventIndexRange = 1..cupTypeNum

        fun getEventName(index: Int): String {
            return "${eventNamePrefix}${index}"
        }

        val events: List<String> = eventIndexRange.map {
            getEventName(it)
        }

        companion object {
            fun makeCupGenerator(
                name: String,
                cupTypeNum: Int = 4,
            ): CupGenerator {
                return CupGenerator(name, cupTypeNum)
            }
        }

        override fun getDevName() = name
    }

    class ConveyorBelt(
        val name: String,
        val locationNum: Int = 4,
        val opLocationList: ArrayList<Int> = arrayListOf(3, 4),
    ) : IDev {
        val locationNamePrefix = "BL"
        val locationIndexRange = 0..locationNum

        fun getLName(index: Int): String {
            return "${locationNamePrefix}${index}"
        }

        val locationsNames: List<String> = locationIndexRange.map {
            getLName(it)
        }

        companion object {
            fun makeConveyorBelt(
                name: String,
                num: Int = 4,
                opLocationList: ArrayList<Int> = arrayListOf(3, 4),
            ) = ConveyorBelt(name, num, opLocationList)
        }

        override fun getDevName() = name
    }

    class MechanicalArm(
        val name: String,
        val moveTimeArr: ArrayList<Int> = arrayListOf(3, 1),
    ) : IDev {
        companion object {
            fun makeMechanicalArm(
                name: String,
                moveTimeArr: ArrayList<Int> = arrayListOf(3, 1),
            ) = MechanicalArm(name, moveTimeArr)
            //0，普通模式，3秒
            //1，加速模式，1秒
        }

        val moveModeRange = moveTimeArr.indices

        fun getMoveTime(k: Int): Int {
            return moveTimeArr[k]
        }

        override fun getDevName() = name
    }

    class LidTuple(
        val lidTypeNum: Int = 4
    ) {
        companion object {
            fun makeLidTuple(lidTypeNum: Int = 4) = LidTuple(lidTypeNum)
        }

        val lidTypeRange = 1..lidTypeNum
    }

    class T4Env(override val timeNeedEnd: Int = 72) : ScxmlEnv() {
        companion object {
            fun makeT4Env(timeNeedEnd: Number = 72) = T4Env(timeNeedEnd.toInt())

            val s1_cupGenerator = makeCupGenerator("s1", 4)
            val s2_conveyorBelt = makeConveyorBelt("s2", 4, arrayListOf(3, 4))
            val s3_mechanicalArm = makeMechanicalArm("s3", arrayListOf(3, 1))

            val aDevs: List<IDev> by lazy {
                listOf(s1_cupGenerator, s2_conveyorBelt, s3_mechanicalArm)
            }

            val aDevsNames: List<String> by lazy {
                aDevs.map {
                    it.getDevName()
                }
            }

            val lidTuple = makeLidTuple(4)
            val moveCostTable = arrayListOf(
                arrayListOf(1, 2), // L1到3号位和4号位的消耗
                arrayListOf(2, 3), // L2到3号位和4号位的消耗
                arrayListOf(2, 1), // L3到3号位和4号位的消耗
                arrayListOf(3, 2)  // L4到3号位和4号位的消耗
            )

            /**
             * 根据输入的i和j查询消耗，其中i的范围为1-4，代表L1-L4，j的范围为3-4，代表3号位和4号位。
             * @param i 查询的起始位置，1-4之间的值，代表L1-L4。
             * @param j 查询的目标位置，3-4之间的值，代表3号位和4号位。
             * @return 从i到j的消耗，如果查询无效则返回-1。
             */
            fun queryCost(i: Int, j: Int): Int {
                // 调整索引以适应moveCostTable的0基索引
                val adjustedI = i - 1
                val adjustedJ = j - 3

                // 验证i和j的范围是否有效
                if (adjustedI !in 0..3 || adjustedJ !in 0..1) return -1

                // 根据adjustedI和adjustedJ查询消耗
                return moveCostTable[adjustedI][adjustedJ]
            }

            @JvmStatic
            fun main(args: Array<String>) {
                // 示例调用
                println(queryCost(1, 3)) // 查询从L1到3号位的消耗
                println(queryCost(1, 4)) // 查询从L1到4号位的消耗
                println(queryCost(4, 3)) // 查询从L4到3号位的消耗
            }
        }

        override val allVars: List<String> by lazy {
            listOf(tClock) + s2_conveyorBelt.locationsNames + "i" + "j" + "score"
        }

        override val aT1LsStringFun: ArrayList<String>.() -> String by lazy {
            {
                this.sortedBy {
                    when {
                        it.startsWith("s1") -> 0
                        it.startsWith("s2") -> 1
                        it.startsWith("s3") -> 2
                        else -> 4
                    }
                }.joinToString(separator = ",")
            }
        }

        override val aT1CsStringFun: ArrayList<ZoneConstraintHelper.Constraint>.() -> String by lazy {
            {
                this.joinToString(",") joinToString@{
                    val varName = it.getVariablesString()
                    if (varName == "BL0") {
                        return@joinToString ""
                    }
                    "${varName}=${it.getPointD().toInt()}"
                }.replace(",,", ",")
            }
        }

        override val aStateH: T2HierarchyStructure<String> by lazy {
            makeT2ParentChildRelations<String>().also { relations ->
                relations.addRelations(null, "s1", "s2", "s3")
                relations.addRelations("s1", "s10", "s11")
                relations.addRelations("s2", "s20", "s21")
                relations.addRelations("s3", "s30", "s31", "s32", "s33", "s34", "s35")
            }
        }
        override val aStateI: LinkedHashMap<String?, LinkedHashSet<String>> by lazy {
            makeA2LHM<String?, LinkedHashSet<String>>().also { lhm ->
                //并发状态有多个直接孩子状态
                lhm[null] = linkedSetOf("s1", "s2", "s3")
                //复合状态只有1个直接孩子状态
                lhm["s1"] = linkedSetOf("s10")
                lhm["s2"] = linkedSetOf("s20")
                lhm["s3"] = linkedSetOf("s30")
            }
        }

        //如果是并发状态，则需要所有子状态都final，并发状态才算final
        override val aStateF: LinkedHashMap<String, LinkedHashSet<String>> by lazy {
            makeA2LHM<String, LinkedHashSet<String>>().also { lhm ->
                lhm["s1"] = linkedSetOf("s11")
                lhm["s2"] = linkedSetOf("s21")
                lhm["s3"] = linkedSetOf("s35")
            }
        }

        //边
        override val aStateEventStateLHM by lazy {
            val lhm = makeA3LHM<String, String, String>()
            val edgeList = ArrayList<Edge>()
            //s1
            s1_cupGenerator.events.forEach {
                edgeList.add(makeEdge("s10", "s10", it))
            }
            //s2
            edgeList.add(makeEdge("s20", "s20", "e21"))
            //s3
            edgeList.add(makeEdge("s30", "s34", "e304"))
            edgeList.add(makeEdge("s34", "s30", "e340"))
            //ik
            lidTuple.lidTypeRange.forEach { i ->
                s3_mechanicalArm.moveTimeArr.indices.forEach { k ->
                    edgeList.add(makeEdge("s34", "s31", "e341${i}${k}"))
                }
            }
            edgeList.add(makeEdge("s31", "s32", "e312"))
            //jk
            s2_conveyorBelt.opLocationList.forEach { j ->
                s3_mechanicalArm.moveTimeArr.indices.forEach { k ->
                    edgeList.add(makeEdge("s32", "s33", "e323${j}${k}"))
                }
            }
            edgeList.add(makeEdge("s33", "s34", "e334"))
            //
            edgeList.forEach {
                lhm.addIfAbsent(
                    it.locationStart,
                    it.edgeName,
                    it.locationEnd,
                )
            }
            lhm
        }

        //最大停留时间
        override val stateStayMaxLHM by lazy {
            val lhm = makeA2LHM<String, Int>()
            lhm.add("s10", 8)
            lhm.add("s11", 0)
            lhm.add("s20", 4)
            lhm.add("s21", 0)
            lhm.add("s30", 0)
            lhm.add("s31", 3)
            lhm.add("s32", 1)
            lhm.add("s33", 3)
            lhm.add("s34", 1)
            lhm.add("s35", 0)
            lhm
        }

        //进入条件
        override val aStateEnterConstraintLHM by lazy {
            val lhm = makeA2LHM<String, (Config) -> Boolean>()
            lhm
        }

        override val aStateNoteRunResultFunLHM: A2LHM<String, NoteRunResultFun> by lazy {
            val lhm = makeA2LHM<String, NoteRunResultFun>()
            listOf("s10", "s20", "s31").forEach {
                lhm[it] = tNoteRunResultFun
            }
            lhm
        }

        override val eventActionLHM: StringActionListLHM by lazy {
            val lhm: StringActionListLHM = makeStringActionListLHM()
            //物品生成器8秒生成一个
            //生成cup放到传送带即BL位置上
            s1_cupGenerator.run {
                //1,2,3,4
                eventIndexRange.forEach { index ->
//                    println("add event ${getEventName(index)}")
                    lhm.add(
                        getEventName(index),
                        arrayListOf(
                            makeAssignAction("BL0") {
                                index.toDouble()
                            },
                        ),
                    )
                }
            }
            //传送带4秒更新一次
            s2_conveyorBelt.run {
                val actions: ArrayList<IAction> = ArrayList()
                //1,2,3,4
                locationIndexRange.reversed().forEach { index ->
                    val adjustIndex = index
                    if (adjustIndex > 0) {
                        actions.add(
                            makeAssignAction(
                                getLName(adjustIndex),
                                makeDoubleDoubleFunTuple(
                                    getLName(adjustIndex - 1)
                                ) {
                                    it
                                },
                            )
                        )
                    } else {
                        actions.add(
                            makeAssignAction(
                                getLName(adjustIndex),
                                makeVoidDoubleFunTuple {
                                    0.0
                                },
                            )
                        )
                    }
                }
                lhm.add("e21", actions)
            }
            //s3初始化
            lhm.add(
                "e304",
                arrayListOf(makeAssignAction("j") { 3.0 }),
            )
            //s3关联发送事件ik
            lidTuple.lidTypeRange.forEach { i ->
                s3_mechanicalArm.moveModeRange.forEach { k ->
                    lhm.add(
                        "e341${i}${k}",
                        arrayListOf(
                            makeSendAction(
                                makeTimedEvent(
                                    s3_mechanicalArm.getMoveTime(k),
                                    "e312",
                                ),
                            ),
                            makeAssignAction(
                                "i",
                                makeVoidDoubleFunTuple {
                                    i.toDouble()
                                },
                            ),
                        ),
                    )
                }
            }
            //s3关联发送事件jk
            s2_conveyorBelt.opLocationList.forEach { j ->
                s3_mechanicalArm.moveModeRange.forEach { k ->
                    lhm.add(
                        "e323${j}${k}",
                        arrayListOf(
                            makeSendAction(
                                makeTimedEvent(
                                    s3_mechanicalArm.getMoveTime(k),
                                    "e334",
                                ),
                            ),
                            makeAssignAction(
                                "j",
                                makeVoidDoubleFunTuple {
                                    j.toDouble()
                                },
                            ),
                        ),
                    )
                }
            }
            //e312
            lhm.add(
                "e312",
                arrayListOf(
                    makeAssignAction(
                        "score",
                        makeDoubleListDoubleFunTuple(
                            arrayListOf("score", "i", "j")
                        ) { (score, i, j) ->
                            score - queryCost(i.toInt(), j.toInt())
                        },
                    )
                ),
            )
            //e334
            lhm.add(
                "e334",
                arrayListOf(
                    makeAssignAction(
                        "score",
                        makeDoubleListDoubleFunTuple(
                            arrayListOf("score", "i", "j")
                        ) { (score, i, j) ->
                            score - queryCost(i.toInt(), j.toInt()) + 10
                        },
                    ),
                ),
            )
            lhm
        }
    }

    object StrategyObj {
        object Env {
            fun getT1StringEnvStrategyLHM(): A2LHM<String, SubT2SNodeUnit> {
                val lhm = makeA2LHM<String, SubT2SNodeUnit>()
                s1_cupGenerator.let { dev ->
                    val devName = dev.name
                    val env = makeSubT2SNodeUnit()
                    lhm.add(devName, env)
                }
                s2_conveyorBelt.let { dev ->
                    val devName = dev.name
                    val env = makeSubT2SNodeUnit()
                    env.funLHM.add(null) { config: Config ->
                        config.states.first {
                            it.startsWith(devName)
                        }
                    }
                    env.addSNode(
                        null,
                        "s20",
                        linkedMapOf(
                            aConfigTrueFun to makeKREWTuple("1", 4, "e21", 1)
                        ),
                        aHeapNotContainsEventFun,
                    )
                    lhm.add(devName, env)
                }
                s3_mechanicalArm.let { dev ->
                    val devName = dev.name
                    val env = makeSubT2SNodeUnit()
                    env.funLHM.add(null) { config: Config ->
                        config.states.first {
                            it.startsWith(devName)
                        }
                    }
                    env.addSNode(
                        null,
                        "s30",
                        linkedMapOf(
                            aConfigTrueFun to makeKREWTuple("1", 0, "e304", 1)
                        ),
                        aHeapNotContainsEventFun,
                    )
                    lhm.add(devName, env)
                }
                return lhm
            }
        }

        object Ren {
            //只在3号位操作
            fun getT1StringRenStrategyLHM(): A2LHM<String, SubT2SNodeUnit> {
                val lhm = makeA2LHM<String, SubT2SNodeUnit>()
                s1_cupGenerator.let { dev ->
                    val devName = dev.name
                    val ren = makeSubT2SNodeUnit()
                    ren.funLHM.add(null) { config: Config ->
                        config.states.first {
                            it.startsWith(devName)
                        }
                    }
                    "s10".let { state ->
                        val aStringTableLHM = makeA2LHM<String, TimedEventWeightTable>()
                        s1_cupGenerator.eventIndexRange.forEach { theCupType ->
                            aStringTableLHM.add(
                                "${theCupType}",
                                makeTimedEventWeightTable().also {
                                    it.add(
                                        makeREWTuple(
                                            8,
                                            "e1${theCupType}",
                                            1,
                                        ),
                                    )
                                },
                            )
                        }
                        ren.addSNode(
                            null,
                            state,
                            aConfigStringFun@{ config: Config ->
                                val eventIndex = ((config.byNameFindC(tClock).getPointD() / 8).toInt() % 4) + 1
                                val eventIndexStr = "${eventIndex}"
                                if (aHeapNotContainsEventFun(config, "e1${eventIndexStr}")) {
                                    return@aConfigStringFun eventIndexStr
                                }
                                ""
                            },
                            aStringTableLHM,
                        )
                    }
                    lhm.add(devName, ren)
                }
                s2_conveyorBelt.let { dev ->
                    val devName = dev.name
                    val ren = makeSubT2SNodeUnit()
                    lhm.add(devName, ren)
                }
                s3_mechanicalArm.let { dev ->
                    val devName = dev.name
                    val ren = makeSubT2SNodeUnit()
                    ren.funLHM.add(null) { config: Config ->
                        config.states.first {
                            it.startsWith(devName)
                        }
                    }
                    "s34".let { state ->
                        val aStringTableLHM = makeA2LHM<String, TimedEventWeightTable>()
                        s1_cupGenerator.eventIndexRange.forEach { theBL1CupType ->
                            aStringTableLHM.add(
                                "${theBL1CupType}",
                                makeTimedEventWeightTable().also {
                                    it.add(
                                        makeREWTuple(
                                            1,
                                            "e341${theBL1CupType}0",
                                            1,
                                        ),
                                    )
                                },
                            )
                        }
                        ren.addSNode(
                            null,
                            state,
                            { config: Config ->
                                val theBL1CupType = config.varConstraintLHM["BL1"]!!.getPointD().toInt()
                                "${theBL1CupType}"
                            },
                            aStringTableLHM,
                        )
                    }
                    "s32".let { state ->
                        val aStringTableLHM = makeA2LHM<String, TimedEventWeightTable>()
                        s1_cupGenerator.eventIndexRange.forEach { theBL1CupType ->
                            aStringTableLHM.add(
                                "3",
                                makeTimedEventWeightTable().also {
                                    it.add(
                                        makeREWTuple(
                                            1,
                                            "e32330",
                                            1,
                                        ),
                                    )
                                },
                            )
                        }
                        ren.addSNode(
                            null,
                            state,
                            { config: Config ->
                                "3"
                            },
                            aStringTableLHM,
                        )
                    }
                    lhm.add(devName, ren)
                }
                return lhm
            }
        }

        object ER {
            fun makeT4Strategy(
                aStringRenStrategyLHM: A2LHM<String, SubT2SNodeUnit>,
                aStringEnvStrategyLHM: A2LHM<String, SubT2SNodeUnit>,
            ): ERT3SNodeUnit {
                return makeERT3SNodeUnit(
                    aDevsNames,
                    aStringRenStrategyLHM,
                    aStringEnvStrategyLHM,
                )
            }

            fun makeT1T4Strategy(): ERT3SNodeUnit {
                return makeT4Strategy(
                    aStringRenStrategyLHM = getT1StringRenStrategyLHM(),
                    aStringEnvStrategyLHM = getT1StringEnvStrategyLHM(),
                )
            }
        }
    }
}